#include "ScreenManager.h"
#include "GameScreen.h"
#include <list>

ScreenManager::ScreenManager(void)
{
}
ScreenManager::~ScreenManager(void)
{
}
void ScreenManager::DumpScreensToSTD()
{
	SetConsoleColor(IGREEN, RED);
	cout << "Dumping screens from Top-Most[0] to Bottom-Most: " << endl;
	cout << "Active Screens " << GetScreenCount() << endl;

	list<GameScreenPtr>::reverse_iterator it = m_screens.rbegin();
	int screenIndex = 0;
	for(;it != m_screens.rend(); it++)
	{
		//TODO: Probably move this as a screen member function
		cout << "[" << screenIndex++ << "] " << (*it)->GetInternalName() << " ";
		cout << "Update:" << (*it)->_bUpdate() << " ";
		cout << "Draw:" << (*it)->_bDraw() << " ";
		cout << "BlockInput:" << (*it)->_bBlockInput() << " ";
		cout << "BlockUpdate:" << (*it)->_bBlockUpdate() << " ";
		cout << endl;
	}

	SetConsoleColor();
}
bool ScreenManager::Init(IGame *pGame)
{
	m_pGame = pGame;
	if(m_pGame == NULL)
	{
		Error(cout << "Screen Manager initialized with pGame=NULL");
	}

	cout << "Screen manager Initialized" << endl;
	return true;
}
bool ScreenManager::AddScreenAfter(GameScreenPtr spScreen, IGameScreen *pScreenBase)
{
	//Add after
	bool screenFound = false;
	
	list<GameScreenPtr>::iterator it = m_screens.begin();
	for(; it != m_screens.end(); it++)
	{
		if((*it).get() == pScreenBase)
		{	
			//Insert screen before iterator value
			m_screens.insert(it, spScreen);

			screenFound = true;
			break;
		}
	}

	//If base screen was not found
	if(screenFound == false)
	{
		//Add as bottom screen
		m_screens.push_front(spScreen);
	}

	//Report screen addition
	SetConsoleColor(GREEN);
	cout << "Screen Added: " << spScreen->GetInternalName().c_str() << endl; 
	SetConsoleColor();

	//Try to initialize the screen
	if(!InitializeScreen(spScreen))
	{
		//Remove screen from screen list, this works because the shared pointer stil is the same as the one that was added
		m_screens.remove(spScreen);

		return false;
	}

	return true;
}
bool ScreenManager::AddScreenBefore(GameScreenPtr spScreen, IGameScreen *pScreenBase)
{
	//Add after
	bool screenFound = false;
	list<GameScreenPtr>::iterator i;
	for(i = m_screens.begin(); i != m_screens.end(); i++)
	{
		if((*i).get() == pScreenBase)
		{
			list<GameScreenPtr>::iterator insertIt = i++;
			if(insertIt != m_screens.end()) {
				//Insert screen after iterator value
				m_screens.insert(insertIt, spScreen);
			} else {
				//Insert as top screen
				m_screens.push_back(spScreen);
			}

			screenFound = true;
			break;
		}
	}

	//If base screen was not found
	if(screenFound == false)
	{
		//Add as top screen
		m_screens.push_back(spScreen);
	}

	//Report screen addition
	SetConsoleColor(GREEN);
	cout << "Screen Added: " << spScreen->GetInternalName().c_str() << endl; 
	SetConsoleColor();

	//Try to initialize the screen
	if(!InitializeScreen(spScreen))
	{
		//Remove screen from screen list, this works because the shared pointer stil is the same as the one that was added
		m_screens.remove(spScreen);

		return false;
	}

	return true;
}
bool ScreenManager::InitializeScreen(GameScreenPtr spScreen)
{
	bool screenInit = spScreen->ScreenInit(m_pGame);

	if(screenInit)
	{
		SetConsoleColor(GREEN);
		cout << "Successfully initialized screen: " << spScreen->GetInternalName().c_str() << endl;
		SetConsoleColor();
	}else{
		Error(cout << "Failed to initialize screen " <<
			spScreen->GetInternalName().c_str() << endl);
	}

	return screenInit;
}
void ScreenManager::Render()
{
	list<GameScreenPtr>::iterator i;
	for(i = m_screens.begin(); i != m_screens.end(); i++)
	{
		IGameScreen *pScreen = (*i).get();
		
		if(pScreen->_bDraw())
		{
			pScreen->Render();
		}
	}
}
void ScreenManager::Update()
{
	list<GameScreenPtr>::reverse_iterator ri;
	bool update = true;
	for(ri = m_screens.rbegin(); ri != m_screens.rend(); ri++)
	{
		IGameScreen *pScreen = (*ri).get();

		if(pScreen->_bUpdate())
		{
			pScreen->Update();
		}
		if(pScreen->_bBlockUpdate())
		{
			ri = m_screens.rend();
		}
	}
}
void ScreenManager::Cleanup()
{
	list<GameScreenPtr>::iterator it = m_screens.begin();
	for(;it != m_screens.end(); it++)
	{
		(*it)->OnScreenRemoved();

		//Report screen removal
		SetConsoleColor(GREEN);
		cout << "Screen Removed: " << (*it)->GetInternalName().c_str() << endl; 
		SetConsoleColor();
	}
	m_screens.clear();
}
uint ScreenManager::GetScreenCount() 
{
	return m_screens.size();
}
void ScreenManager::RemoveScreen(IGameScreen *pScreen)
{
	list<GameScreenPtr>::iterator it = m_screens.begin();
	for(; it != m_screens.end(); it++)
	{
		if((*it).get() == pScreen)
		{
			(*it)->OnScreenRemoved();

			//Report screen removal
			SetConsoleColor(GREEN);
			cout << "Screen Removed: " << pScreen->GetInternalName().c_str() << endl; 
			SetConsoleColor();

			m_screens.remove((*it));
			return;
		}
	}
}
void ScreenManager::OnKeyPress(uint key)
{
	list<GameScreenPtr> _temp;
	copy(m_screens.begin(), m_screens.end(), back_inserter(_temp));
	list<GameScreenPtr>::reverse_iterator it = _temp.rbegin();
	for(; it != _temp.rend(); it++)
	{
		(*it)->OnKeyPress(key);
		//Check if screen blocks input
		if((*it)->_bBlockInput())
			it = _temp.rend();
	}
}
void ScreenManager::OnKeyRelease(uint key)
{
	list<GameScreenPtr> _temp;
	copy(m_screens.begin(), m_screens.end(), back_inserter(_temp));
	list<GameScreenPtr>::reverse_iterator it = _temp.rbegin();
	for(; it != _temp.rend(); it++)
	{
		(*it)->OnKeyRelease (key);
		//Check if screen blocks input
		if((*it)->_bBlockInput())
			it = _temp.rend();
	}
}
uint ScreenManager::GetKeyMod()
{
	return SDL_GetModState();
}
GameScreenPtr ScreenManager::GetScreenAt(int index)
{
	return GameScreenPtr((IGameScreen*)NULL);
}
GameScreenPtr ScreenManager::GetTopScreen()
{
	return GameScreenPtr((IGameScreen*)NULL);
}
GameScreenPtr ScreenManager::GetBaseScreen()
{
	return GameScreenPtr((IGameScreen*)NULL);
}
void ScreenManager::RequestScreenRemoval(IGameScreen *pScreen)
{
	RemoveScreen(pScreen);
}